package com.didiglobal.turbo.demo.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.didiglobal.turbo.demo.pojo.po.EmFormTemplate;
import com.didiglobal.turbo.demo.pojo.request.GetFlowModuleListRequest;
import com.didiglobal.turbo.demo.pojo.response.BaseResponse;
import com.didiglobal.turbo.demo.service.EmFormTemplateService;
import com.didiglobal.turbo.demo.service.LeaveServiceImpl;
import com.didiglobal.turbo.engine.bo.NodeInstance;
import com.didiglobal.turbo.engine.common.ErrorEnum;
import com.didiglobal.turbo.engine.common.FlowElementType;
import com.didiglobal.turbo.engine.common.FlowInstanceStatus;
import com.didiglobal.turbo.engine.common.NodeInstanceStatus;
import com.didiglobal.turbo.engine.dao.FlowDeploymentDAO;
import com.didiglobal.turbo.engine.dao.InstanceDataDAO;
import com.didiglobal.turbo.engine.dao.NodeInstanceDAO;
import com.didiglobal.turbo.engine.dao.ProcessInstanceDAO;
import com.didiglobal.turbo.engine.dao.mapper.ProcessInstanceMapper;
import com.didiglobal.turbo.engine.engine.ProcessEngine;
import com.didiglobal.turbo.engine.entity.FlowDeploymentPO;
import com.didiglobal.turbo.engine.entity.FlowInstancePO;
import com.didiglobal.turbo.engine.entity.InstanceDataPO;
import com.didiglobal.turbo.engine.entity.NodeInstancePO;
import com.didiglobal.turbo.engine.model.FlowElement;
import com.didiglobal.turbo.engine.model.FlowModel;
import com.didiglobal.turbo.engine.model.InstanceData;
import com.didiglobal.turbo.engine.param.CommitTaskParam;
import com.didiglobal.turbo.engine.param.RollbackTaskParam;
import com.didiglobal.turbo.engine.param.StartProcessParam;
import com.didiglobal.turbo.engine.result.*;
import com.didiglobal.turbo.engine.util.FlowModelUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zhanglu
 * @project turbo-main
 * @description 任务
 * @date 2023/3/26 13:35
 */
@RestController
@RequestMapping("/flow-task")
public class TaskController {

    private static final Logger LOGGER = LoggerFactory.getLogger(TaskController.class);

    @Resource
    private ProcessEngine processEngine;
    @Resource
    private LeaveServiceImpl leaveService;
    @Resource
    private ProcessInstanceDAO processInstanceDAO;
    @Resource
    private InstanceDataDAO instanceDataDAO;
    @Resource
    private FlowDeploymentDAO flowDeploymentDAO;
    @Resource
    private EmFormTemplateService formTemplateService;
    @Resource
    private ProcessInstanceMapper processInstanceMapper;
    @Resource
    private NodeInstanceDAO nodeInstanceDAO;

    public TaskController() {
    }

    /**
     * 发起流程
     * @return
     */
    @GetMapping("/t1")
    public BaseResponse t1(){
        leaveService.run();
        return BaseResponse.make(null);
    }

    /**
     * 发起流程-查看表单模版
     * @param flowDeployId
     * @return
     *  - 模版定义：json
     *  - 流程审批数据
     */
    @GetMapping("/form-template")
    public BaseResponse formTemplate(@RequestParam String flowDeployId){
        //根据flowDeployId查询流程发布表，获取开始节点的表单模版id
        return formTemplate(flowDeployId, null);
    }

    /**
     * 任务流程-查看表单
     * @param flowInstanceId
     * @return
     *  - 模版定义：json
     *  - 流程审批数据
     */
    @GetMapping("/form-info")
    public BaseResponse formInfo(@RequestParam String flowInstanceId){
        //流程审批数据：根据实例，查询最近的nodeInstanceData
        //模版：根据flow_deploy_id查询flow_model，根据nodeKey，获取properties，从而获取到template_id
        Map<String, Object> res = new HashMap<>();
        InstanceDataPO instanceDataPO = instanceDataDAO.selectRecentOne(flowInstanceId);
        if(instanceDataPO == null){
            return BaseResponse.make(null).setErrMsg("未配置表单模版");
        }
        res.put("flow_instance_id", instanceDataPO.getFlowInstanceId());
        res.put("variables", JSONArray.parseArray(instanceDataPO.getInstanceData(), Map.class)
                .stream().collect(Collectors.toMap(o -> o.get("key"), o -> o.get("value"))));
        res.put("form_template", formTemplate(instanceDataPO.getFlowDeployId(), instanceDataPO.getNodeKey()).getData());
        FlowInstancePO flowInstancePO = processInstanceDAO.selectByFlowInstanceId(flowInstanceId);
        if(flowInstancePO.getStatus() == FlowInstanceStatus.COMPLETED){
            return BaseResponse.make(res).setErrCode(-1).setErrMsg("已审批完成");
        } else if(flowInstancePO.getStatus() == FlowInstanceStatus.TERMINATED){
            return BaseResponse.make(res).setErrCode(-1).setErrMsg("已驳回");
        }
        return BaseResponse.make(res);
    }

    /**
     * 查看当前流程详情
     * @param flowInstanceId
     * @return
     */
    @GetMapping("/current-detail")
    public BaseResponse currentDetail(@RequestParam String flowInstanceId){
        Map<String, Object> res = new HashMap<>();
        NodeInstancePO nodeInstancePO = nodeInstanceDAO.selectRecentOne(flowInstanceId);
        FlowInstancePO instancePO = processInstanceDAO.selectByFlowInstanceId(flowInstanceId);
        FlowDeploymentPO flowDeploymentPO = flowDeploymentDAO.selectByDeployId(instancePO.getFlowDeployId());
        if(nodeInstancePO == null || flowDeploymentPO == null){
            return BaseResponse.make(null);
        }
        NodeInstanceListResult historyUserTaskList = processEngine.getHistoryUserTaskList(flowInstanceId);
        res.put("historyUserTaskList", historyUserTaskList);
        if(historyUserTaskList != null){
            List<NodeInstance> nodes = historyUserTaskList.getNodeInstanceList().stream().sorted(Comparator.comparing(NodeInstance::getCreateTime)).collect(Collectors.toList());
            res.put("currentTaskName", StrUtil.join(" -> ", nodes.stream()
                    .map(o -> {
                        String statusStr = "";
                        if(Objects.equals(o.getStatus(), NodeInstanceStatus.COMPLETED)){
                            statusStr = "通过";
                        }else if(Objects.equals(o.getStatus(), NodeInstanceStatus.ACTIVE)) {
                            statusStr = "处理中";
                        }else if(Objects.equals(o.getStatus(), NodeInstanceStatus.FAILED)) {
                            statusStr = "驳回";
                        }else if(Objects.equals(o.getStatus(), NodeInstanceStatus.DISABLED)) {
                            statusStr = "回退";
                        }
                        return StrUtil.format("【{}({})】", o.getModelName(), statusStr);
                    }).collect(Collectors.toList())));
        }else {
            res.put("historyUserTaskList", "-");
        }
        res.put("currentNode", nodeInstancePO);
        res.put("flowDeployment", flowDeploymentPO);
        return BaseResponse.make(res);
    }

    /**
     * 发起流程
     *  - 保存表单实例
     *    - startProcess：新增：ei_flow_instance（2）、ei_instance_data、ei_node_instance、ei_node_instance_log
     *    - commitTask：新增：ei_instance_data、更新：ei_node_instance（上个任务节点2 -> 1）、新增：ei_node_instance、ei_node_instance_log；若完成，则更新任务实例ei_flow_instance（2->1）
     *    - rollbackTask：更新：ei_node_instance（上个任务节点1->4）、新增：ei_node_instance（上一个任务节点1）、ei_node_instance_log
     * @param flowDeployId  流程部署id
     * @param formParams    表单参数
     * @return
     */
    @PostMapping("/start-process")
    public StartProcessResult startProcess(@RequestParam String flowDeployId, @RequestBody Map<String, Object> formParams){
        StartProcessParam startProcessParam = new StartProcessParam();
        startProcessParam.setFlowDeployId(flowDeployId);
        List<InstanceData> variables = new ArrayList<>();
        formParams.forEach((k, v) ->  variables.add(new InstanceData(k, v)));
        startProcessParam.setVariables(variables);
        StartProcessResult startProcessResult = processEngine.startProcess(startProcessParam);
        startProcessResult.getActiveTaskInstance().getNodeInstanceId();
        LOGGER.info("startProcess.||startProcessResult={}", startProcessResult);
        startProcessResult.setErrCode(ErrorEnum.SUCCESS.getErrNo());
        startProcessResult.setErrMsg(ErrorEnum.SUCCESS.getErrMsg());
        return startProcessResult;
    }

    /**
     * 提交任务-审批通过
     */
    @PostMapping("/commit-do")
    public CommitTaskResult doCommit(@RequestParam String flowInstanceId,
                                     @RequestBody Map<String, Object> formParams){
        CommitTaskParam commitTaskParam = new CommitTaskParam();
        commitTaskParam.setFlowInstanceId(flowInstanceId);
        commitTaskParam.setTaskInstanceId(nodeInstanceDAO.selectRecentOne(flowInstanceId).getNodeInstanceId());
        List<InstanceData> variables = new ArrayList<>();
        formParams.forEach((k, v) ->  variables.add(new InstanceData(k, v)));
        commitTaskParam.setVariables(variables);
        CommitTaskResult commitTaskResult = processEngine.commitTask(commitTaskParam);
        LOGGER.info("inputTime.||commitTaskResult={}", commitTaskResult);
        commitTaskResult.setErrCode(ErrorEnum.SUCCESS.getErrNo());
        commitTaskResult.setErrMsg(ErrorEnum.SUCCESS.getErrMsg());
        return commitTaskResult;
    }

    /**
     * 回退到上级
     * @param flowInstanceId
     * @return
     */
    @PostMapping("/rollback-do")
    public RollbackTaskResult doRollback(@RequestParam String flowInstanceId){
        RollbackTaskParam rollbackTaskParam = new RollbackTaskParam();
        rollbackTaskParam.setFlowInstanceId(flowInstanceId);
        rollbackTaskParam.setTaskInstanceId(nodeInstanceDAO.selectRecentOne(flowInstanceId).getNodeInstanceId());
        RollbackTaskResult rollbackTaskResult = processEngine.rollbackTask(rollbackTaskParam);
        LOGGER.info("rollbackToInputTime.||rollbackTaskResult={}", rollbackTaskResult);
        rollbackTaskResult.setErrCode(ErrorEnum.SUCCESS.getErrNo());
        rollbackTaskResult.setErrMsg(ErrorEnum.SUCCESS.getErrMsg());
        return rollbackTaskResult;
    }

    /**
     * 驳回
     * @param flowInstanceId
     * @return
     */
    @PostMapping("/reject-do")
    public BaseResponse<RollbackTaskResult> doReject(@RequestParam String flowInstanceId){
        processInstanceDAO.updateStatus(flowInstanceId, FlowInstanceStatus.TERMINATED);
        return BaseResponse.make(null);
    }

    /**
     * 办理流程列表
     * @param request
     * @return
     */
    @PostMapping("/get/process-instance/list")
    public BaseResponse processInstanceList(@RequestBody GetFlowModuleListRequest request){
        return BaseResponse.make(processInstanceMapper.selectPage(new Page<>(request.getCurrent(), request.getSize()),null));
    }

    /**
     * 查看用户流程实例执行的历史任务
     * @param flowInstanceId
     * @return
     */
    @GetMapping("/history/user-list")
    public BaseResponse<NodeInstanceListResult> historyUserTaskList(@RequestParam String flowInstanceId){
        NodeInstanceListResult historyUserTaskList = processEngine.getHistoryUserTaskList(flowInstanceId);
        return BaseResponse.make(historyUserTaskList);
    }

    /**
     * 查看用户流程实例的元素
     * @param flowInstanceId
     * @return
     */
    @GetMapping("/history/element-list")
    public BaseResponse<ElementInstanceListResult> historyUserElementList(@RequestParam String flowInstanceId){
        ElementInstanceListResult historyElementList = processEngine.getHistoryElementList(flowInstanceId);
        return BaseResponse.make(historyElementList);
    }

    private BaseResponse<EmFormTemplate> formTemplate(String flowDeployId, String nodeKey){
        //根据flowDeployId查询流程发布表，获取开始节点的表单模版id
        if(flowDeployId == null){
            BaseResponse response = BaseResponse.make(null);
            response.setErrMsg("未部署");
            return response;
        }
        FlowDeploymentPO flowDeploymentPO = flowDeploymentDAO.selectByDeployId(flowDeployId);
        FlowModel flowModel = FlowModelUtil.parseModelFromString(flowDeploymentPO.getFlowModel());
        Optional<FlowElement> first = flowModel.getFlowElementList().stream().filter(o -> {
            if(nodeKey == null || "".equals(nodeKey)){
                return Objects.equals(o.getType(), FlowElementType.START_EVENT);
            }else {
                return Objects.equals(o.getKey(), nodeKey);
            }
        }).findFirst();
        if(!first.isPresent() || !first.get().getProperties().containsKey("form_template_id")){
            BaseResponse response = BaseResponse.make(null);
            response.setErrMsg("未配置表单模版");
            return response;
        }
        FlowElement flowElement = first.get();
        Object formTemplateId = flowElement.getProperties().get("form_template_id");
        EmFormTemplate formTemplate = formTemplateService.getById(formTemplateId.toString());
        return BaseResponse.make(formTemplate);
    }


}
